<?php

namespace common\base;

use Yii;
use yii\base\ErrorException;
use yii\base\Exception;
use yii\base\UserException;
use yii\web\HttpException;
use yii\web\Response;
use common\helpers\Debug;

/**
 * ErrorHandler handles uncaught PHP errors and exceptions.
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class ErrorHandler extends \yii\web\ErrorHandler {

    /**
     * @var boolean $custom Wether an exception to custom return.
     */
    public $custom = true;

    /**
     * Converts an exception into an array.
     *
     * @param \Exception|\Error $exception the exception being converted
     * @return array the array representation of the exception.
     * @throws \yii\base\InvalidConfigException
     */
    protected function convertExceptionToArray($exception) {
        if ($this->custom) {
            return $this->convertCustomException($exception);
        }
        return parent::convertExceptionToArray($exception);
    }

    /**
     * 自定义异常处理
     *
     * @param \Exception|\Error $exception the exception being converted
     * @return array the array representation of the exception.
     * @throws \yii\base\InvalidConfigException
     */
    protected function convertCustomException($exception) {
        if (Yii::$app->has('response')) {
            $response = Yii::$app->getResponse();
        } else {
            $response = new Response();
        }
        $response->setStatusCode(200);
        if (!$exception instanceof UserException && !$exception instanceof HttpException) {
            $exception = new HttpException(500, Yii::t('yii', 'An internal server error occurred.'));
        }
        $array = [
            'code' => $exception->statusCode,
            'msg' => $exception->getMessage(),
        ];
        if (YII_DEBUG) {
            $array['_debug'] = $this->debugRequest();
            $trace = [
                'code' => $exception->getCode(),
                'name' => ($exception instanceof Exception || $exception instanceof ErrorException) ? $exception->getName() : 'Exception',
                'type' => get_class($exception),
            ];
            if (!$exception instanceof UserException) {
                $trace['file'] = $exception->getFile();
                $trace['line'] = $exception->getLine();
                $trace['stack-trace'] = explode("\n", $exception->getTraceAsString());
                if ($exception instanceof \yii\db\Exception) {
                    $trace['error-info'] = $exception->errorInfo;
                }
            }
            $prev = $exception->getPrevious();
            if ($prev !== null) {
                $trace['previous'] = $this->convertExceptionToArray($prev);
            }
            $array['_trace'] = $trace;
        }
        return $array;
    }

    /**
     * 调试模式下追加调试信息
     *
     * @inheritdoc
     * @throws \yii\base\InvalidConfigException
     */
    protected function debugRequest() {
        $request = Yii::$app->getRequest();
        $headers = $request->getHeaders()->toArray();
        foreach ($headers as $key => $vls) {
            $headers[$key] = $vls[0];
        }
        return [
            'method' => $request->getMethod(),
            'url' => $request->getUrl(),
            'queryParams' => $request->getQueryParams(),
            'bodyParams' => $request->getBodyParams(),
            'headers' => $headers,
        ];
    }

    /**
     * Logs the given exception.
     * @param \Exception $exception the exception to be logged
     * @since 2.0.3 this method is now public.
     */
    public function logException($exception) {
        $category = get_class($exception);
        if ($exception instanceof HttpException) {
            $category = 'yii\\web\\HttpException:' . $exception->statusCode;
            Debug::dumpRequest($category);
        } elseif ($exception instanceof \ErrorException) {
            $category .= ':' . $exception->getSeverity();
        }
        Yii::error($exception, $category);
    }

}
